﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Murry.DataStore.MemoryDB
{
    /// <summary>
    /// Store object in memory using IDataStore interface.
    /// </summary>
    public class MemoryDatabase
    {
        /// <summary>
        /// Dictionary of objects by type and id.
        /// </summary>
        protected Dictionary<Type, Dictionary<string, MemoryPersist>> Objects = new Dictionary<Type, Dictionary<string, MemoryPersist>>();

        /// <summary>
        /// Find object by ID.
        /// </summary>
        /// <typeparam name="TInterface">Interface Type.</typeparam>
        /// <param name="implementedType">Implemented Type.</param>
        /// <param name="id">Object ID</param>
        /// <returns>Object</returns>
        public TInterface FindById<TInterface>(Type implementedType, string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException("id");
            }

            if (!Objects.ContainsKey(implementedType) || !Objects[implementedType].ContainsKey(id))
            {
                throw new ArgumentException("Object ID not found.");
            }

            return (TInterface)Objects[implementedType][id].Clone();
        }

        /// <summary>
        /// Add an object. 
        /// </summary>
        /// <param name="memoryPersist">Object to add.</param>
        public void Add(MemoryPersist memoryPersist)
        {
            if (!string.IsNullOrEmpty(memoryPersist.Id))
            {
                throw new InvalidOperationException("Add failed, object ID has already been set.");
            }

            if (!Objects.ContainsKey(memoryPersist.GetType()))
            {
                Objects[memoryPersist.GetType()] = new Dictionary<string, MemoryPersist>();
            }

            memoryPersist.Id = Guid.NewGuid().ToString();

            Objects[memoryPersist.GetType()][memoryPersist.Id] = (MemoryPersist)memoryPersist.Clone();
        }

        /// <summary>
        /// Update an object. 
        /// </summary>
        /// <param name="memoryPersist">Object to update.</param>
        public void Update(MemoryPersist memoryPersist)
        {
            if (string.IsNullOrEmpty(memoryPersist.Id))
            {
                throw new InvalidOperationException("Update failed, object ID has not been set.");
            }

            if (!Objects.ContainsKey(memoryPersist.GetType()) || !Objects[memoryPersist.GetType()].ContainsKey(memoryPersist.Id))
            {
                throw new InvalidOperationException("Update failed, object not found by ID.");
            }

            Objects[memoryPersist.GetType()][memoryPersist.Id] = (MemoryPersist)memoryPersist.Clone();
        }

        /// <summary>
        /// Delete an object. 
        /// </summary>
        /// <param name="memoryPersist">Object to delete.</param>
        public void Delete(MemoryPersist memoryPersist)
        {
            if (string.IsNullOrEmpty(memoryPersist.Id))
            {
                throw new InvalidOperationException("Delete failed, object ID has not been set.");
            }

            if (!Objects.ContainsKey(memoryPersist.GetType()) || !Objects[memoryPersist.GetType()].ContainsKey(memoryPersist.Id))
            {
                throw new InvalidOperationException("Delete failed, object not found by ID.");
            }

            Objects[memoryPersist.GetType()].Remove(memoryPersist.Id);
        }

        /// <summary>
        /// Get a Queryable interface for a given implemented type.
        /// </summary>
        /// <typeparam name="TInterface">Interface type.</typeparam>
        /// <param name="implementedType">Implemented type.</param>
        /// <returns>Queryable interface.</returns>
        public IQueryable<TInterface> GetQueryable<TInterface>(Type implementedType)
        {
            if (!Objects.ContainsKey(implementedType))
            {
                Objects[implementedType] = new Dictionary<string, MemoryPersist>();
            }

            return new CloningQueryable<MemoryPersist>(Objects[implementedType].Values.AsQueryable()).Cast<TInterface>();
        }
    }
}
